package sess

import (
	"context"
	"net"
	"sync/atomic"
	"time"

	"gitee.com/night-tc/gobige/common"
	"gitee.com/night-tc/gobige/global"
	"gitee.com/night-tc/gobige/logger"
	"gitee.com/night-tc/gobige/msgdef"
	"gitee.com/night-tc/gobige/msgdef/protomsg"
	"gitee.com/night-tc/gobige/msghandler"
	"gitee.com/night-tc/gobige/pool"
	"gitee.com/night-tc/gobige/safecontainer"
	"gitee.com/night-tc/gobige/threads"
)

/*
一个连接会话
*/
type NetSess struct {
	//消息管理器（如果是服务器会话，它就是service层的，如果是用户的连接，他就是用户设置进来的）
	msgh msghandler.IMsgHandler
	//连接
	conn net.Conn
	//发送消息的队列
	sendBuf *safecontainer.SafeListT[any]
	//管理协程器
	thgo *threads.SubThreadGo
	//超时主动断开连接
	timeout *time.Timer
	//心跳定时器（主动连接对方面的时候，会使用）
	hbTicker *time.Ticker
	// 延迟关闭连接（发现顶好的时候，需要等一下关链接）
	closeTimer *time.Timer

	//表示连接的ID，一般与上层的服务器ID或是用户ID一致
	id uint64
	//如果连接是服务器过来的话，那就是服务器类型，2表示客户端
	stype global.ServerTypeEnum
	//是否加密
	encryEnabled bool
	//是否自己是客户端
	isClient bool
	//是否验证过
	isVerified bool
	//是否关闭了
	isClose atomic.Bool
	// 是否延迟关闭了
	isDelayClose atomic.Bool
	// 是否需要打印
	_isInputMsg atomic.Bool
	_isRecvMsg  atomic.Bool

	//超时时间
	timeoutV time.Duration
	//心跳时间
	hbTickerV time.Duration

	//管理器
	callback ISessCallBack
}

/*
新建连接会话
conn:连接
cb：发生对应事件的时候的回调对象
isclient:管理器为空的时候
encryEnabled：是否加密
*/
func newNetSess(conn net.Conn, cb ISessCallBack, thgo *threads.ThreadGo, isClient bool, encryEnabled bool) (result *NetSess) {
	result = new(NetSess)
	result.sendBuf = safecontainer.NewSafeListT[any]()

	result.conn = conn
	result.thgo = threads.NewSubThreadGo(thgo)

	result.encryEnabled = encryEnabled
	result.isClient = isClient

	result.timeoutV = global.GetCfgInst().GetSessTimeout()
	result.hbTickerV = global.GetCfgInst().GetSessTicker()
	result.callback = cb

	return result
}

////////////////////////////////////////////////////////////////
/* 外部接口 */

/* 外部接口 */
////////////////////////////////////////////////////////////////
/* 私有函数 */

// 发数据到连接中，还有控制连接的关闭逻辑
func (this *NetSess) sendLoop(ctx context.Context) {
	defer func() {
		this.Close()
		this.conn.Close()
	}()
	/*
		这是发消息到连接上的协程
		连接关闭的时候，也要把消息都发出去
		除非是从ctx收到 的关闭消息
		二个定时器如果不用的话，需要初始化出来
		不然会报错的
	*/
	if this.isClient {
		// this.hbTicker = time.NewTicker(this.hbTickerV)
		defer this.hbTicker.Stop()
		// this.timeout = &time.Timer{}
	} else {
		// this.hbTicker = &time.Ticker{}
		// this.timeout = time.NewTimer(this.timeoutV)
		defer this.timeout.Stop()
	}
	this.closeTimer = time.NewTimer(time.Hour)
	this.closeTimer.Stop()
	for {
		select {
		case <-ctx.Done():
			{
				return
			}
		case <-this.timeout.C:
			{
				logger.Infof("[NetSess] timeout Addr:%s id:%d type:%d;", this.conn.RemoteAddr(), this.id, this.stype)
				this.Close()
			}
		case <-this.hbTicker.C:
			{
				this.Send(&protomsg.HeartBeat{})
			}
		case <-this.closeTimer.C:
			{
				this.Close()
			}
		case <-this.sendBuf.C:
			this.loopsendMsg()
		}
	}
}

func (this *NetSess) loopsendMsg() {
	isinputmsg := this._isInputMsg.Load()
	buf := pool.Get(MaxMsgBuffer)
	defer pool.Put(buf)
	st := pool.NewByteSteam(buf)
	defer func() {
		_ = this.sendMsg(st.GetDataBuff())
	}()

	for {
		data, err := this.sendBuf.Pop()
		if err != nil {
			break
		}
		switch m := data.(type) {
		case msgdef.IMsg:
			{
				if m.Name() == "Hangup" {
					_ = this.sendMsg(st.GetDataBuff())
					// 唯一关闭逻辑
					this.thgo.Cal()
					return
				}
				// 勇哥说，如果消息加完后小余1000字节的话，有效果
				if st.GetLen()+m.Size()+MsgHeadSize > 1000 {
					if err = this.sendMsg(st.GetDataBuff()); err != nil {
						st.ReSet()
						this.thgo.Cal()
						return
					}
					st.ReSet()
				}
				nbuf, err := EncodeMsgWithEncrypt(m, st.GetNilBuff(), true, this.encryEnabled)
				if err != nil {
					logger.Warnf("encode err:%s msg:%s", err, m)
					continue
				}
				st.AddLen(len(nbuf))
				if isinputmsg {
					this.logMsg(m, nbuf)
				}
			}
		case []byte:
			{
				if st.GetLen()+len(m) > 1000 {
					if err = this.sendMsg(st.GetDataBuff()); err != nil {
						st.ReSet()
						this.thgo.Cal()
						return
					}
					st.ReSet()
				}
				st.Copy(m)
				if isinputmsg {
					tmp := make([]byte, len(m))
					copy(tmp, m)
					msg, err := DecodeBytes(tmp)
					if err != nil {
						logger.Warn(err)
					} else {
						this.logMsg(msg, tmp)
					}
				}
			}
		}
	}
}

func (this *NetSess) logMsg(msg msgdef.IMsg, tmp []byte) {
	switch v := msg.(type) {
	case *protomsg.HeartBeat:
	case *protomsg.EntityMsgRet:
		rpcmsg, err := msgdef.GetTypeMgr().NewMsgByID(uint16(v.MsgID), v.MsgContent)
		if err != nil {
			logger.Debugf(logger.LogKey_Msg, "send msg %d	EntityMsgRet:%d %v", len(tmp), v.MsgID, err)
		} else {
			logger.Debugf(logger.LogKey_Msg, "send msg %d	EntityMsgRet:%s %v", len(tmp), rpcmsg.Name(), rpcmsg)
		}
	default:
		logger.Debugf(logger.LogKey_Msg, "send msg %d	%s %v", len(tmp), msg.Name(), msg)
	}
}

func (this *NetSess) sendMsg(msgBuf []byte) error {
	if len(msgBuf) == 0 {
		return nil
	}
	_, err := this.conn.Write(msgBuf)
	if err != nil {
		logger.Warnf(" sendMsg errno: %v ", err)
		return err

	}
	return nil
}

// 从连接中读数据
func (this *NetSess) recvLoop(ctx context.Context) {
	/*
		如果我是服务端监听建立的会话，那需要验证对方是谁
		连接过来的时候，就会启动
		所以验证的逻辑需要在这里调用
	*/
	if !this.isClient {
		msg, _, err := readARQMsgForward(this.conn)
		//如果是连接扫描就强制关掉
		if err != nil || msg == nil {
			logger.Error("accept conn and  read first message error ", err, this.conn.RemoteAddr())
			this.Close()
			return
		}

		var im msgdef.ISessionVerifyReqMsg
		var ok bool
		if im, ok = msg.(msgdef.ISessionVerifyReqMsg); !ok {
			logger.Error("[SessMgr] acceptConn read first message , but message is not client verify req ", this.conn.RemoteAddr())
			this.Close()
			return
		}
		err = this.callback.SessVerify(im, this)
		if err != nil {
			logger.Info("[SessMgr] acceptConn client verify failed", this.conn.RemoteAddr(), err)
			this.Close()
			return
		}
		_ = this.msgh.DoNormalMsg(im.Name(), im)

	}

	/*
	   这个是收消息的协程
	   只要连接坏了，就可以掉用关闭逻辑
	*/
	for {
		msg, msgID, err := readARQMsgForward(this.conn)
		if err != nil {
			if this.isClose.Load() {
				return
			}
			if err.Error() == "EOF" {
				logger.Infof("[NetSess] recvLoop sess[%v] disconnect .", this.conn.RemoteAddr())
			} else {
				logger.Errorf("[NetSess] recvLoop tcp read message error errno:%d msg:%v addr:%s id:%d ",
					common.GetErrno(err), err, this.conn.RemoteAddr(), this.GetID())
			}
			this.Close()
			return
		} else if msg != nil {
			if msg.Name() == "HeartBeat" {
				//如果我不是客户端，我就发回去
				if !this.isClient {
					this.touch()
					this.Send(msg)
				}
			}
			if this._isRecvMsg.Load() {
				switch m := msg.(type) {
				case *protomsg.HeartBeat:
				// case *protomsg.th:
				// 	args := serializer.UnSerialize(m.MsgData)
				// 	logger.Debugf(logger.LogKey_Msg, "recv msg :%s to linker,msg:%v", m.MsgName, args)
				case *protomsg.EntityRPCReq:
					args, err := DecodeMsg(0, m.MsgContent)
					if err != nil {
						logger.Debugf(logger.LogKey_Msg, "recv msg EntityRPCReq:%s srvtype:%d %v", m.PRCName, m.SrvType, args)

					} else {
						logger.Debugf(logger.LogKey_Msg, "recv msg EntityRPCReq:%s srvtype:%d %v", m.PRCName, m.SrvType, args)
					}
				default:
					logger.Debugf(logger.LogKey_Msg, "recv msg %s ", msg.Name())

				}
			}
			if !this.isDelayClose.Load() {
				this.msgh.FireMsg(msg.Name(), msg)
			}
		} else if msgID != 0 {
			//消息没有解出来
			logger.Warnf("Unknown message msgID:%d", msgID)
			this.Close()
			return
		} else {
			//不可能到这里来
			this.Close()
			return
		}

	}
}

func (this *NetSess) touch() {
	//服务器才有超时
	if !this.isClient {
		this.timeout.Reset(this.timeoutV)
	}
}

/* 私有函数 */
////////////////////////////////////////////////////////////////
/* 属性 */

// 设置消息路由 正常情况下都是使用外部的，验证前使用管理器的，验证后使用对象的
func (this *NetSess) SetMsgHandler(msgh msghandler.IMsgHandler) {
	this.msgh = msgh
}

// 设置表示连接的ID，一般与上层的服务器ID或是用户ID一致
func (this *NetSess) SetID(v uint64) {
	this.id = v
}

// 表示连接的ID，一般与上层的服务器ID或是用户ID一致
func (this *NetSess) GetID() uint64 {
	return this.id
}

// 如果连接是服务器过来的话，那就是服务器类型，2表示客户端
func (this *NetSess) GetType() global.ServerTypeEnum {
	return this.stype
}

// 设置如果连接是服务器过来的话，那就是服务器类型，2表示客户端
func (this *NetSess) SetType(stype global.ServerTypeEnum) {
	this.stype = stype
}

// 远程地址
func (this *NetSess) RemoteAddr() string {
	return this.conn.RemoteAddr().String()
}

// 是否验证过
func (this *NetSess) IsVerified() bool {
	return this.isVerified
}

// 设置验证过
func (this *NetSess) SetVerify() {
	this.isVerified = true
}

// 设置是否输出 发出的消息
func (this *NetSess) SetInputMsg(v bool) {
	this._isInputMsg.Store(v)
}

// 设置是否输出 收到的消息
func (this *NetSess) SetRecvMsg(v bool) {
	this._isRecvMsg.Store(v)
}

// 延时关闭连接
func (this *NetSess) DelayClose() {
	if this.isClose.Load() {
		return
	}
	if this.isDelayClose.CompareAndSwap(false, true) {
		this.closeTimer.Reset(time.Second * 5)
	}
}

/* 属性 */
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
